// 定义 reactive 函数
// 1.get返回响应式对象，收集依赖
// 2.set和deleteProperty返回修改状态，触发更新
// 3.返回一个Proxy实例
const isObject = target => target !== null && typeof target === "object";
const convert = target => (isObject(target) ? reactive(target) : target);
const hasOwn = (target, key) => target.hasOwnProperty(key);
/**
 * @method reactive
 * @param {object | string} target
 * @return {Proxy}
 */
export function reactive(target) {
    // 判断 target 不是 object 直接返回
    if (!isObject(target)) return target;

    const handler = {
        get(target, key, receiver) {
            // 收集依赖
            track(target, key)
            const result = Reflect.get(target, key, receiver);
            return convert(result);
        },
        set(target, key, value, receiver) {
            // 判断新值和旧值是否相等
            const oldValue = Reflect.get(target, key, receiver);
            if (oldValue !== value) {
                Reflect.set(target, key, value, receiver);
                // 触发更新
                trigger(target, key)
            }
            return true;
        },
        deleteProperty(target, key) {
            // 判断对象是否存在需要删除的属性
            const hadKey = hasOwn(target, key);
            const result = Reflect.deleteProperty(target, key);
            if (hadKey && result) {
                // 触发更新
                trigger(target, key)
            }
            return result;
        },
    };

    return new Proxy(target, handler);
}

let activeEffect = null;
export function effect(callback) {
    activeEffect = callback;
    callback(); // 访问响应式对象属性，去收集依赖
    activeEffect = null;
}

let targetMap = new WeakMap();
export function track(target, key) {
    if (!activeEffect) return;
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
        depsMap.set(key, (dep = new Set()));
    }
    dep.add(activeEffect);
}

export function trigger(target, key) {
    const depsMap = targetMap.get(target)
    if (!depsMap) return
    const dep = depsMap.get(key)
    if (!dep) return
    dep.forEach(effect => {
        effect()
    })
}

export function ref(raw) {
    if (isObject(raw) && raw.__v_isRef) return

    let value = convert(raw)

    const r = {
        __v_isRef: true,
        get value() {
            track(r, 'value')
            return value
        },
        set value(newValue) {
            if (newValue !== value) {
                raw = newValue
                value = convert(raw)
                trigger(r, 'value')
            }
        }
    }
    return r
}

export function toRefs (proxy) {
    const ret = proxy instanceof Array ? new Array(proxy,length) : {}

    for (const key in proxy) {
        ret[key] = toProxyRef(proxy, key)
    }
    return ret
}

function toProxyRef(proxy, key) {
    const r = {
        __v_isRef: true,
        get value() {
            return proxy[key]
        },
        set value(newValue) {
            proxy[key] = newValue
        }
    }
    return r
}

export function computed(getter) {
    let result = ref()
    effect(() => result.value = getter())
    return result
}